-
Notifications
You must be signed in to change notification settings - Fork 27
🤖 refactor: migrate IPC layer to ORPC for type-safe RPC #763
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
0106889 to
a1e6608
Compare
Replace Electron's loosely-typed ipcMain/ipcRenderer with ORPC, providing
end-to-end type safety between frontend and backend.
Key changes:
- Add @orpc/client, @orpc/server, @orpc/zod dependencies
- Create ORPC router (src/node/orpc/router.ts) with typed procedures
- Add React ORPC provider and useORPC hook (src/browser/orpc/react.tsx)
- Extract services: WorkspaceService, ProjectService, ProviderService,
TokenizerService, TerminalService, WindowService, UpdateService
- Replace window.api.* calls with typed client.* calls throughout frontend
- Update test infrastructure to use ORPC test client (orpcTestClient.ts)
- Switch Jest transform from ts-jest to babel-jest for ESM compatibility
Breaking: Removes src/common/types/ipc.ts and src/common/constants/ipc-constants.ts
in favor of src/common/orpc/types.ts and src/common/orpc/schemas.ts
_Generated with mux_
Change-Id: Ibfeb8345e27baf663ca53ae04e4906621fda3b62
Signed-off-by: Thomas Kosiewski <tk@coder.com>
🤖 refactor: complete ORPC migration Phase 5 cleanup
- Delete obsolete src/browser/api.test.ts (tested legacy invokeIPC pattern)
- Update src/desktop/preload.ts comment to reflect ORPC architecture
- Remove unused StreamErrorType re-export from src/common/orpc/types.ts
_Generated with mux_
Change-Id: I27a79252ee4256558f4aab8a3c4d60d7820d6599
Signed-off-by: Thomas Kosiewski <tk@coder.com>
🤖 fix: fix E2E test failures after ORPC migration
1. Fix ORPCProvider platform detection: check for window.api existence
instead of window.api.platform === 'electron' (preload exposes
process.platform which is 'darwin'/'win32'/'linux', not 'electron')
2. Fix E2E stream capture: replace assert() with inline throw since
page.evaluate() stringifies code and loses import references
_Generated with mux_
Change-Id: I9e4b35b830cea0d689845c2f4f2e68653f756e3d
Signed-off-by: Thomas Kosiewski <tk@coder.com>
🤖 test: fix E2E /compact test expectation
Remove outdated '📦 compacted' expectation - the compaction feature
now shows only summary text without the label marker.
_Generated with mux_
Change-Id: Ic43a3dd9d099545a58832ebf60183775843f697f
Signed-off-by: Thomas Kosiewski <tk@coder.com>
🤖 fix: migrate WorkspaceConsumerManager to use ORPC client for tokenization
The tokenizer was still using the old window.api.tokenizer bridge which no longer
exists after the ORPC migration. Updated to use window.__ORPC_CLIENT__.tokenizer
instead.
This fixes the repeated 'Tokenizer IPC bridge unavailable' assertion errors
during E2E tests.
Change-Id: I43820079337ca98e0dc97e863cde9414536d107f
Signed-off-by: Thomas Kosiewski <tk@coder.com>
🤖 fix: fix flaky E2E toast tests with longer duration in E2E mode
Changes:
- Expose isE2E flag via preload to renderer for E2E-specific behavior
- Increase toast auto-dismiss duration from 3s to 10s in E2E mode
- Add sendCommandAndExpectStatus helper that waits for toast concurrently
- Disable fullyParallel for Electron tests to avoid timing issues
- Update tests to use new helper for reliable toast assertions
The root cause was that toasts auto-dismiss after 3 seconds, but under
parallel test execution the timing variance meant assertions could miss
observing the toast before it disappeared.
Change-Id: I
Signed-off-by: Thomas Kosiewski <tk@coder.com>
🤖 fix: fix StreamCollector race condition in integration tests
The StreamCollector was marking the subscription as ready immediately after
getting the async iterator, but the ORPC generator body (which sets up the
actual subscription) doesn't run until iteration starts.
Changes:
- Add waitForSubscription() method that waits for first event
- Mark subscription as ready after receiving first event (from history replay)
- Add small delay after subscription ready to stabilize under load
- Update sendMessageAndWait to use the new synchronization
This fixes flaky integration tests in runtimeFileEditing.test.ts where
streaming events were sometimes missed due to the race condition.
Change-Id: I1f697dbf9486a45c9335fd00c42fb54853715ed3
Signed-off-by: Thomas Kosiewski <tk@coder.com>
🤖 fix: restore native terminal opening for Electron desktop mode
The ORPC migration inadvertently changed the terminal opening behavior:
- Before: Clicking terminal button opened the user's native terminal app
(Ghostty, Terminal.app, etc.) with cwd set to workspace path
- After: It opened an xterm.js web terminal in an Electron popup window
This restores the original behavior by:
1. Adding TerminalService.openNative() method with platform-specific logic:
- macOS: Ghostty (if available) or Terminal.app
- Windows: cmd.exe
- Linux: x-terminal-emulator, ghostty, alacritty, kitty, etc.
2. Adding ORPC endpoint terminal.openNative for the new method
3. Updating useOpenTerminal hook to call openNative for Electron mode
The web terminal (openWindow) is still available for browser mode.
Added comprehensive unit tests to prevent this regression:
- Tests for macOS Terminal.app and Ghostty detection
- Tests for Windows cmd opening
- Tests for Linux terminal emulator discovery
- Tests for SSH workspace handling
- Tests for error conditions
Change-Id: Ib01af78cab49cb6ed3486eaaee85277f4b3daa15
Signed-off-by: Thomas Kosiewski <tk@coder.com>
🤖 fix: guard against undefined event.key in matchesKeybind
Certain keyboard events (dead keys for accents, modifier-only events, etc.)
can have event.key as undefined, causing a TypeError when calling toLowerCase().
Added defensive check to return false early when event.key is falsy.
Added unit tests for the keybinds utility.
Change-Id: I3784275ea2f0bd1206c548e3014854f259bc7a3e
Signed-off-by: Thomas Kosiewski <tk@coder.com>
🤖 refactor: rename IpcMain to ServiceContainer and fix review issues
- Rename IpcMain class to ServiceContainer to reflect its actual purpose
as a dependency container for ORPC services
- Move tests/ipcMain/ to tests/integration/ for clarity
- Fix provider config: empty string values now delete keys (allows clearing API keys)
- Fix WorkspaceContext: add missing `client` dependency in createWorkspace
- Fix schemas: add missing compacted/cmuxMetadata fields, remove stale entries
- Fix updater: remove unused mainWindow field and setMainWindow method
Change-Id: Iea939ecdcbb986f5a4f38a8cd2d7f250e8497dcf
Signed-off-by: Thomas Kosiewski <tk@coder.com>
🤖 fix: guard TitleBar against missing window.api in browser mode
Change-Id: Ic6d1ddef2d3a9e3b047d1d6598e583d4ca345c57
Signed-off-by: Thomas Kosiewski <tk@coder.com>
cleanup
Change-Id: Ia6374d2f4e3696709536c93b2488d4bf0f3fda0f
Signed-off-by: Thomas Kosiewski <tk@coder.com>
🤖 feat: add auth middleware to oRPC router
Add bearer token authentication for HTTP and WebSocket endpoints using
oRPC's native middleware pattern. Headers are injected into context at
the transport layer, allowing a unified middleware to handle both.
- Create authMiddleware.ts with createAuthMiddleware and extractWsHeaders
- Update ORPCContext to include optional headers field
- Apply auth middleware to router via t.use()
- Inject headers into context in orpcServer.ts for HTTP and WS
- Support WS auth fallbacks: query param, Authorization header, protocol
Change-Id: Ief9b8b6d03d1f0161b996ac5d88ce2807e910c94
Signed-off-by: Thomas Kosiewski <tk@coder.com>
fix: return actual path from listDirectory, not empty string
The listDirectory function was using buildFileTree() which creates a
synthetic root with name: '' and path: ''. This broke DirectoryPickerModal
which relies on root.path for:
- Displaying the current directory path in the UI
- Computing parent directory via ${root.path}/..
- Returning the selected path to the caller
Fixed by returning a FileTreeNode with the resolved absolute path as both
name and path, matching the original IPC handler behavior.
Added regression tests to prevent this from happening again.
Change-Id: Iaddcbc3982c4f2440bcd92420e295881bf4fe90c
Signed-off-by: Thomas Kosiewski <tk@coder.com>
When the server requires authentication (--auth-token), the browser client now shows a modal prompting the user to enter the auth token. The token is: - Stored in localStorage for subsequent visits - Can also be passed via URL query parameter (?token=...) - Cleared and re-prompted if authentication fails This replaces the previous server-side injection approach with a cleaner user-driven authentication flow. Change-Id: I5599266df30340bcc5ca016a14a67a5d74c52669 Signed-off-by: Thomas Kosiewski <tk@coder.com>
…ovided Fixes Storybook tests failing in CI. When a client prop is passed to ORPCProvider, the component now starts in "connected" state immediately instead of waiting for useEffect. This prevents a flash of null content that caused tests to see an empty storybook-root div. Change-Id: I048104e7f2fe434efcf9b50db0bae445d912b014 Signed-off-by: Thomas Kosiewski <tk@coder.com>
The ORPC migration changed ReviewPanel to use client.workspace.executeBash() instead of window.api.workspace.executeBash(). Update the story to provide a mock ORPC client via ORPCProvider instead of mocking window.api. _Generated with mux_ Change-Id: Icd3cc9eb6a4ce6f9aebb7d8e72e8627d7220f740 Signed-off-by: Thomas Kosiewski <tk@coder.com>
e24c438 to
5edd40e
Compare
- Replace custom API client with ORPC client in mobile app - Split monolithic schemas.ts into domain-specific schema files - Derive TypeScript types from Zod schemas (single source of truth) - Add exhaustive handler map for chat events with TypedEventType - Fix executeBash result unwrapping (was assuming double-wrapped Result) - Add usage-delta handler (silently ignored on mobile) _Generated with mux_ Change-Id: Ia4a52a091b4a9273ee1a1484336c00ac9a145edc Signed-off-by: Thomas Kosiewski <tk@coder.com>
5edd40e to
0d73943
Compare
The parseRuntimeModeAndHost function was trying to parse "ssh user@host" as a RuntimeMode, which failed because the schema only accepts "ssh" or "local". Now checks for the SSH prefix first before attempting mode parsing. Change-Id: Ia8722ea6febdad1fc95eddba5fcb9d4d5c076932 Signed-off-by: Thomas Kosiewski <tk@coder.com>
The ORPC migration PR (#763) inadvertently deleted the auto-compaction trigger logic when sending messages. This restores: - Check if usage >= threshold before sending regular messages - Execute compaction with continueMessage instead of direct send - Show 'Context threshold reached - auto-compacting...' toast - Proper error handling with input/image restoration The supporting infrastructure (checkAutoCompaction, CompactionWarning, settings UI) remained intact - only the trigger logic was missing. _Generated with `mux`_
Reverts: - 470e4eb perf: fix streaming content delay from ORPC schema validation (#774) - b437977 feat: add backend support for soft-interrupts (#767) - df30cbc fix: use ResultSchema for sendMessage output to prevent field stripping (#773) - 41c77ef fix: testUtils formatting (#771) - 3ee7288 refactor: migrate IPC layer to ORPC for type-safe RPC (#763)
Reverts: - 470e4eb perf: fix streaming content delay from ORPC schema validation (#774) - b437977 feat: add backend support for soft-interrupts (#767) - df30cbc fix: use ResultSchema for sendMessage output to prevent field stripping (#773) - 41c77ef fix: testUtils formatting (#771) - 3ee7288 refactor: migrate IPC layer to ORPC for type-safe RPC (#763) Due to a huge number of regressions. _Generated with `mux`_
This commit reintroduces the ORPC refactoring that was originally merged in #763 and subsequently reverted in #777 due to regressions. ## Original PR: #763 Replaces the custom IPC layer with oRPC for type-safe RPC between browser/renderer and backend processes. ## Why it was reverted (#777) The original migration caused regressions including: - Streaming content delay from ORPC schema validation - Field stripping issues in sendMessage output - Auto-compaction trigger deletion ## What's different this time - Rebased onto latest main which includes fixes that were developed post-revert (model favorites, auto-compaction, etc.) - Conflict resolution preserves upstream features added after revert: - Workspace name collision retry with hash suffix - Mux Gateway coupon code handling with default models - AWS Bedrock credential nested structure ## Key Changes ### Architecture - New ORPC router (src/node/orpc/router.ts) - Central router with Zod schemas - Schema definitions (src/common/orpc/schemas.ts) - Shared validation - ServiceContainer (src/node/services/serviceContainer.ts) - DI container - React integration (src/browser/orpc/react.tsx) - ORPCProvider and useORPC() ### Transport - Desktop (Electron): MessagePort-based RPC via @orpc/server/message-port - Server mode: HTTP + WebSocket via @orpc/server/node and @orpc/server/ws - Auth middleware with timing-safe token comparison ### Removed - src/browser/api.ts (old HTTP/WS client) - src/node/services/ipcMain.ts (old IPC handler registration) - Old IPC method definitions in preload.ts --- _Generated with mux_
This commit reintroduces the ORPC refactoring that was originally merged in #763 and subsequently reverted in #777 due to regressions. ## Original PR: #763 Replaces the custom IPC layer with oRPC for type-safe RPC between browser/renderer and backend processes. ## Why it was reverted (#777) The original migration caused regressions including: - Streaming content delay from ORPC schema validation - Field stripping issues in sendMessage output - Auto-compaction trigger deletion ## What's different this time - Rebased onto latest main which includes fixes that were developed post-revert (model favorites, auto-compaction, etc.) - Conflict resolution preserves upstream features added after revert: - Workspace name collision retry with hash suffix - Mux Gateway coupon code handling with default models - AWS Bedrock credential nested structure ## Key Changes ### Architecture - New ORPC router (src/node/orpc/router.ts) - Central router with Zod schemas - Schema definitions (src/common/orpc/schemas.ts) - Shared validation - ServiceContainer (src/node/services/serviceContainer.ts) - DI container - React integration (src/browser/orpc/react.tsx) - ORPCProvider and useORPC() ### Transport - Desktop (Electron): MessagePort-based RPC via @orpc/server/message-port - Server mode: HTTP + WebSocket via @orpc/server/node and @orpc/server/ws - Auth middleware with timing-safe token comparison ### Removed - src/browser/api.ts (old HTTP/WS client) - src/node/services/ipcMain.ts (old IPC handler registration) - Old IPC method definitions in preload.ts --- _Generated with mux_
This commit reintroduces the ORPC refactoring that was originally merged in #763 and subsequently reverted in #777 due to regressions. ## Original PR: #763 Replaces the custom IPC layer with oRPC for type-safe RPC between browser/renderer and backend processes. ## Why it was reverted (#777) The original migration caused regressions including: - Streaming content delay from ORPC schema validation - Field stripping issues in sendMessage output - Auto-compaction trigger deletion ## What's different this time - Rebased onto latest main which includes fixes that were developed post-revert (model favorites, auto-compaction, etc.) - Conflict resolution preserves upstream features added after revert: - Workspace name collision retry with hash suffix - Mux Gateway coupon code handling with default models - AWS Bedrock credential nested structure ## Key Changes ### Architecture - New ORPC router (src/node/orpc/router.ts) - Central router with Zod schemas - Schema definitions (src/common/orpc/schemas.ts) - Shared validation - ServiceContainer (src/node/services/serviceContainer.ts) - DI container - React integration (src/browser/orpc/react.tsx) - ORPCProvider and useORPC() ### Transport - Desktop (Electron): MessagePort-based RPC via @orpc/server/message-port - Server mode: HTTP + WebSocket via @orpc/server/node and @orpc/server/ws - Auth middleware with timing-safe token comparison ### Removed - src/browser/api.ts (old HTTP/WS client) - src/node/services/ipcMain.ts (old IPC handler registration) - Old IPC method definitions in preload.ts --- _Generated with mux_
This commit reintroduces the ORPC refactoring that was originally merged in #763 and subsequently reverted in #777 due to regressions. ## Original PR: #763 Replaces the custom IPC layer with oRPC for type-safe RPC between browser/renderer and backend processes. ## Why it was reverted (#777) The original migration caused regressions including: - Streaming content delay from ORPC schema validation - Field stripping issues in sendMessage output - Auto-compaction trigger deletion ## What's different this time - Rebased onto latest main which includes fixes that were developed post-revert (model favorites, auto-compaction, etc.) - Conflict resolution preserves upstream features added after revert: - Workspace name collision retry with hash suffix - Mux Gateway coupon code handling with default models - AWS Bedrock credential nested structure ## Key Changes ### Architecture - New ORPC router (src/node/orpc/router.ts) - Central router with Zod schemas - Schema definitions (src/common/orpc/schemas.ts) - Shared validation - ServiceContainer (src/node/services/serviceContainer.ts) - DI container - React integration (src/browser/orpc/react.tsx) - ORPCProvider and useORPC() ### Transport - Desktop (Electron): MessagePort-based RPC via @orpc/server/message-port - Server mode: HTTP + WebSocket via @orpc/server/node and @orpc/server/ws - Auth middleware with timing-safe token comparison ### Removed - src/browser/api.ts (old HTTP/WS client) - src/node/services/ipcMain.ts (old IPC handler registration) - Old IPC method definitions in preload.ts --- _Generated with mux_
This commit reintroduces the ORPC refactoring that was originally merged in #763 and subsequently reverted in #777 due to regressions. ## Original PR: #763 Replaces the custom IPC layer with oRPC for type-safe RPC between browser/renderer and backend processes. ## Why it was reverted (#777) The original migration caused regressions including: - Streaming content delay from ORPC schema validation - Field stripping issues in sendMessage output - Auto-compaction trigger deletion ## What's different this time - Rebased onto latest main which includes fixes that were developed post-revert (model favorites, auto-compaction, etc.) - Conflict resolution preserves upstream features added after revert: - Workspace name collision retry with hash suffix - Mux Gateway coupon code handling with default models - AWS Bedrock credential nested structure ## Key Changes ### Architecture - New ORPC router (src/node/orpc/router.ts) - Central router with Zod schemas - Schema definitions (src/common/orpc/schemas.ts) - Shared validation - ServiceContainer (src/node/services/serviceContainer.ts) - DI container - React integration (src/browser/orpc/react.tsx) - ORPCProvider and useORPC() ### Transport - Desktop (Electron): MessagePort-based RPC via @orpc/server/message-port - Server mode: HTTP + WebSocket via @orpc/server/node and @orpc/server/ws - Auth middleware with timing-safe token comparison ### Removed - src/browser/api.ts (old HTTP/WS client) - src/node/services/ipcMain.ts (old IPC handler registration) - Old IPC method definitions in preload.ts --- _Generated with mux_
This commit reintroduces the ORPC refactoring that was originally merged in #763 and subsequently reverted in #777 due to regressions. ## Original PR: #763 Replaces the custom IPC layer with oRPC for type-safe RPC between browser/renderer and backend processes. ## Why it was reverted (#777) The original migration caused regressions including: - Streaming content delay from ORPC schema validation - Field stripping issues in sendMessage output - Auto-compaction trigger deletion ## What's different this time - Rebased onto latest main which includes fixes that were developed post-revert (model favorites, auto-compaction, etc.) - Conflict resolution preserves upstream features added after revert: - Workspace name collision retry with hash suffix - Mux Gateway coupon code handling with default models - AWS Bedrock credential nested structure ## Key Changes ### Architecture - New ORPC router (src/node/orpc/router.ts) - Central router with Zod schemas - Schema definitions (src/common/orpc/schemas.ts) - Shared validation - ServiceContainer (src/node/services/serviceContainer.ts) - DI container - React integration (src/browser/orpc/react.tsx) - ORPCProvider and useORPC() ### Transport - Desktop (Electron): MessagePort-based RPC via @orpc/server/message-port - Server mode: HTTP + WebSocket via @orpc/server/node and @orpc/server/ws - Auth middleware with timing-safe token comparison ### Removed - src/browser/api.ts (old HTTP/WS client) - src/node/services/ipcMain.ts (old IPC handler registration) - Old IPC method definitions in preload.ts --- _Generated with mux_ fix: add @babel/preset-react for JSX transpilation in Jest tests fix: add missing timestamp field to ToolCallEndEvent schema and usages fix: use ref for ORPCProvider cleanup to avoid stale closure The useEffect cleanup captured state at mount time ('connecting'), so even after transitioning to 'connected', cleanup never ran. Now stores cleanup function in a ref that's always current. test: add feature branch to storybook mock branch list Extend listBranches mock response with a feature branch example to better represent realistic branch listings during component development and testing. fix: batch async message yields to prevent scroll issues The oRPC async generator was yielding messages one at a time with async boundaries, causing premature React renders during history replay. This led to scroll-to-bottom firing before all messages loaded. Extract createAsyncMessageQueue utility that yields all queued messages synchronously (no async pauses within a batch). Used by both the real router and storybook mocks to match original IPC callback behavior. fix: add missing type discriminators to storybook chat messages The MarkdownTables story was emitting messages without the required `type: "message"` field. The `as WorkspaceChatMessage` type casts hid this from TypeScript, causing messages to not be recognized by the chat processor and rendering "No Messages Yet" instead. Also fix ChatEventProcessor tests that had invalid event shapes: - Remove invalid `role`/`timestamp` from stream-start events - Add missing `workspaceId`/`tokens` to delta events fix: use useLayoutEffect for initial scroll to fix Chromatic snapshots The scroll-to-bottom when workspace loads was using useEffect with requestAnimationFrame, which runs asynchronously after browser paint. Chromatic could capture the snapshot before scroll completed. Switch to useLayoutEffect which runs synchronously after DOM mutations but before paint, ensuring scroll happens before Chromatic captures. This also removes the need for the RAF wrapper since useLayoutEffect already guarantees DOM is ready. fix: add play functions to ensure Chromatic captures scrolled state - Add waitForChatScroll play function that waits for messages to load and scroll to complete before Chromatic takes screenshots - Add data-testid="message-window" to scroll container in AIView - Add data-testid="chat-message" to message wrappers for test queries - Apply play function to ActiveWorkspaceWithChat and MarkdownTables stories - Fix ProviderIcon lint error: remove redundant "mux-gateway" union type (already included in ProviderName) fix: resolve React Native typecheck errors - Add missing 'error' event handler in normalizeChatEvent.ts Converts error events to stream-error for mobile display - Fix result.metadata access in WorkspaceScreen.tsx ResultSchema wraps data, so access result.data.metadata not result.metadata - Add RequestInitWithDispatcher type in aiService.ts Extends RequestInit with undici-specific dispatcher property for Node.js fix: use queueMicrotask instead of setTimeout in story mocks Replace setTimeout(..., 100) with queueMicrotask() for message delivery in Storybook mocks. This ensures messages arrive synchronously (in the microtask queue) rather than 100ms later, eliminating timing races with Chromatic's screenshot capture. - All onChat mock callbacks now use queueMicrotask for message delivery - Add chromatic: { delay: 500 } as backup for ActiveWorkspaceWithChat and MarkdownTables stories - Fix import: use storybook/test instead of @storybook/test (Storybook 10) fix: remove deprecated @storybook/addon-interactions from config In Storybook 10, interaction testing is built into the core and the addon-interactions package was deprecated. Remove it from the addons list to fix build failures caused by version mismatch. fix: add semver 7.x to fix storybook build in CI Storybook 10 imports semver/functions/sort.js which only exists in semver 7.x. The root-level semver was 6.x (needed by babel), causing ESM resolution in Node 20.x to fail in CI. Adding semver ^7.x as a direct dependency forces the root-level version to 7.x, which is backwards compatible with 6.x consumers. fix: add missing fields to oRPC provider and workspace schemas The ProviderConfigInfoSchema was missing couponCodeSet (for Mux Gateway) and aws (for Bedrock). oRPC strips fields not in the schema, so these values were never reaching the frontend UI. Also fixes workspace.fork to use FrontendWorkspaceMetadataSchema instead of WorkspaceMetadataSchema, ensuring namedWorkspacePath is not stripped. refactor: consolidate provider types to single source of truth Eliminates triple-definition of ProviderConfigInfo/AWSCredentialStatus types that existed in providerService.ts, Settings/types.ts, and the oRPC schema. Now the Zod schema is the single source of truth, with TypeScript types derived via z.infer. Adds conformance tests that validate oRPC schemas preserve all fields when parsing - this would have caught the missing couponCodeSet/aws fields bug. refactor: improve tool part schema type safety and remove dead code 1. Refactor MuxToolPartSchema using extend pattern for DRY code: - Base schema shares common fields (type, toolCallId, toolName, input, timestamp) - Pending variant: state="input-available", no output field - Available variant: state="output-available", output required 2. Remove dead ReasoningStartEventSchema: - Schema was defined but never emitted by backend - Not in WorkspaceChatMessageSchema union - No type guard existed for it 3. Update MuxToolPart type to use Zod inference: - Type now properly discriminates based on state - Accessing output requires narrowing to output-available state refactor: simplify tool output redaction with type narrowing Remove explicit DynamicToolPart cast in favor of TypeScript's built-in type narrowing after the discriminant check. The type guard `part.type !== "dynamic-tool"` already narrows the type, making the cast redundant. Also eliminate intermediate variables (toolPart, redacted) by returning the new object directly, reducing visual noise. fix: resolve race condition in queued messages test Capture event count before interrupt to establish baseline, then poll for new events rather than waiting for next event. The clear event may arrive before or simultaneously with stream-abort, causing the previous waitForQueuedMessageEvent call to miss it or timeout. refactor: rename ORPC types and hooks to generic API naming Rename ORPCClient to APIClient, useORPC to useAPI, and ORPCProvider to APIProvider. Move the module from src/browser/orpc/react.tsx to src/browser/hooks/useAPI.tsx to better reflect its purpose as a general API client abstraction rather than being tied to the oRPC implementation detail. This decouples the public interface from the underlying transport mechanism, making future backend changes transparent to consumers. refactor: expose connection state from useAPI hook with discriminated union Changed useAPI from returning just the client to returning a discriminated union with connection state, enabling consumers to handle loading/error states with skeleton loaders instead of blocking the entire UI. Changes: - useAPI now returns `{ api, status, error, authenticate, retry }` - `api` is `APIClient | null` based on connection state (type-safe) - `status` is one of: "connecting", "connected", "auth_required", "error" - When `status === "connected"`, TypeScript knows `api` is non-null - Auth modal moved to App.tsx (rendered after all hooks) - Moved file from hooks/useAPI.tsx to contexts/API.tsx (follows codebase convention where Context+Provider+hook combinations live in contexts/) Updated ~60 consumer files with null guards before API calls.
This commit reintroduces the ORPC refactoring that was originally merged in #763 and subsequently reverted in #777 due to regressions. ## Original PR: #763 Replaces the custom IPC layer with oRPC for type-safe RPC between browser/renderer and backend processes. ## Why it was reverted (#777) The original migration caused regressions including: - Streaming content delay from ORPC schema validation - Field stripping issues in sendMessage output - Auto-compaction trigger deletion ## What's different this time - Rebased onto latest main which includes fixes that were developed post-revert (model favorites, auto-compaction, etc.) - Conflict resolution preserves upstream features added after revert: - Workspace name collision retry with hash suffix - Mux Gateway coupon code handling with default models - AWS Bedrock credential nested structure ## Key Changes ### Architecture - New ORPC router (src/node/orpc/router.ts) - Central router with Zod schemas - Schema definitions (src/common/orpc/schemas.ts) - Shared validation - ServiceContainer (src/node/services/serviceContainer.ts) - DI container - React integration (src/browser/orpc/react.tsx) - ORPCProvider and useORPC() ### Transport - Desktop (Electron): MessagePort-based RPC via @orpc/server/message-port - Server mode: HTTP + WebSocket via @orpc/server/node and @orpc/server/ws - Auth middleware with timing-safe token comparison ### Removed - src/browser/api.ts (old HTTP/WS client) - src/node/services/ipcMain.ts (old IPC handler registration) - Old IPC method definitions in preload.ts --- _Generated with mux_ fix: add @babel/preset-react for JSX transpilation in Jest tests fix: add missing timestamp field to ToolCallEndEvent schema and usages fix: use ref for ORPCProvider cleanup to avoid stale closure The useEffect cleanup captured state at mount time ('connecting'), so even after transitioning to 'connected', cleanup never ran. Now stores cleanup function in a ref that's always current. test: add feature branch to storybook mock branch list Extend listBranches mock response with a feature branch example to better represent realistic branch listings during component development and testing. fix: batch async message yields to prevent scroll issues The oRPC async generator was yielding messages one at a time with async boundaries, causing premature React renders during history replay. This led to scroll-to-bottom firing before all messages loaded. Extract createAsyncMessageQueue utility that yields all queued messages synchronously (no async pauses within a batch). Used by both the real router and storybook mocks to match original IPC callback behavior. fix: add missing type discriminators to storybook chat messages The MarkdownTables story was emitting messages without the required `type: "message"` field. The `as WorkspaceChatMessage` type casts hid this from TypeScript, causing messages to not be recognized by the chat processor and rendering "No Messages Yet" instead. Also fix ChatEventProcessor tests that had invalid event shapes: - Remove invalid `role`/`timestamp` from stream-start events - Add missing `workspaceId`/`tokens` to delta events fix: use useLayoutEffect for initial scroll to fix Chromatic snapshots The scroll-to-bottom when workspace loads was using useEffect with requestAnimationFrame, which runs asynchronously after browser paint. Chromatic could capture the snapshot before scroll completed. Switch to useLayoutEffect which runs synchronously after DOM mutations but before paint, ensuring scroll happens before Chromatic captures. This also removes the need for the RAF wrapper since useLayoutEffect already guarantees DOM is ready. fix: add play functions to ensure Chromatic captures scrolled state - Add waitForChatScroll play function that waits for messages to load and scroll to complete before Chromatic takes screenshots - Add data-testid="message-window" to scroll container in AIView - Add data-testid="chat-message" to message wrappers for test queries - Apply play function to ActiveWorkspaceWithChat and MarkdownTables stories - Fix ProviderIcon lint error: remove redundant "mux-gateway" union type (already included in ProviderName) fix: resolve React Native typecheck errors - Add missing 'error' event handler in normalizeChatEvent.ts Converts error events to stream-error for mobile display - Fix result.metadata access in WorkspaceScreen.tsx ResultSchema wraps data, so access result.data.metadata not result.metadata - Add RequestInitWithDispatcher type in aiService.ts Extends RequestInit with undici-specific dispatcher property for Node.js fix: use queueMicrotask instead of setTimeout in story mocks Replace setTimeout(..., 100) with queueMicrotask() for message delivery in Storybook mocks. This ensures messages arrive synchronously (in the microtask queue) rather than 100ms later, eliminating timing races with Chromatic's screenshot capture. - All onChat mock callbacks now use queueMicrotask for message delivery - Add chromatic: { delay: 500 } as backup for ActiveWorkspaceWithChat and MarkdownTables stories - Fix import: use storybook/test instead of @storybook/test (Storybook 10) fix: remove deprecated @storybook/addon-interactions from config In Storybook 10, interaction testing is built into the core and the addon-interactions package was deprecated. Remove it from the addons list to fix build failures caused by version mismatch. fix: add semver 7.x to fix storybook build in CI Storybook 10 imports semver/functions/sort.js which only exists in semver 7.x. The root-level semver was 6.x (needed by babel), causing ESM resolution in Node 20.x to fail in CI. Adding semver ^7.x as a direct dependency forces the root-level version to 7.x, which is backwards compatible with 6.x consumers. fix: add missing fields to oRPC provider and workspace schemas The ProviderConfigInfoSchema was missing couponCodeSet (for Mux Gateway) and aws (for Bedrock). oRPC strips fields not in the schema, so these values were never reaching the frontend UI. Also fixes workspace.fork to use FrontendWorkspaceMetadataSchema instead of WorkspaceMetadataSchema, ensuring namedWorkspacePath is not stripped. refactor: consolidate provider types to single source of truth Eliminates triple-definition of ProviderConfigInfo/AWSCredentialStatus types that existed in providerService.ts, Settings/types.ts, and the oRPC schema. Now the Zod schema is the single source of truth, with TypeScript types derived via z.infer. Adds conformance tests that validate oRPC schemas preserve all fields when parsing - this would have caught the missing couponCodeSet/aws fields bug. refactor: improve tool part schema type safety and remove dead code 1. Refactor MuxToolPartSchema using extend pattern for DRY code: - Base schema shares common fields (type, toolCallId, toolName, input, timestamp) - Pending variant: state="input-available", no output field - Available variant: state="output-available", output required 2. Remove dead ReasoningStartEventSchema: - Schema was defined but never emitted by backend - Not in WorkspaceChatMessageSchema union - No type guard existed for it 3. Update MuxToolPart type to use Zod inference: - Type now properly discriminates based on state - Accessing output requires narrowing to output-available state refactor: simplify tool output redaction with type narrowing Remove explicit DynamicToolPart cast in favor of TypeScript's built-in type narrowing after the discriminant check. The type guard `part.type !== "dynamic-tool"` already narrows the type, making the cast redundant. Also eliminate intermediate variables (toolPart, redacted) by returning the new object directly, reducing visual noise. fix: resolve race condition in queued messages test Capture event count before interrupt to establish baseline, then poll for new events rather than waiting for next event. The clear event may arrive before or simultaneously with stream-abort, causing the previous waitForQueuedMessageEvent call to miss it or timeout. refactor: rename ORPC types and hooks to generic API naming Rename ORPCClient to APIClient, useORPC to useAPI, and ORPCProvider to APIProvider. Move the module from src/browser/orpc/react.tsx to src/browser/hooks/useAPI.tsx to better reflect its purpose as a general API client abstraction rather than being tied to the oRPC implementation detail. This decouples the public interface from the underlying transport mechanism, making future backend changes transparent to consumers. refactor: expose connection state from useAPI hook with discriminated union Changed useAPI from returning just the client to returning a discriminated union with connection state, enabling consumers to handle loading/error states with skeleton loaders instead of blocking the entire UI. Changes: - useAPI now returns `{ api, status, error, authenticate, retry }` - `api` is `APIClient | null` based on connection state (type-safe) - `status` is one of: "connecting", "connected", "auth_required", "error" - When `status === "connected"`, TypeScript knows `api` is non-null - Auth modal moved to App.tsx (rendered after all hooks) - Moved file from hooks/useAPI.tsx to contexts/API.tsx (follows codebase convention where Context+Provider+hook combinations live in contexts/) Updated ~60 consumer files with null guards before API calls.
This commit reintroduces the ORPC refactoring that was originally merged in #763 and subsequently reverted in #777 due to regressions. ## Original PR: #763 Replaces the custom IPC layer with oRPC for type-safe RPC between browser/renderer and backend processes. ## Why it was reverted (#777) The original migration caused regressions including: - Streaming content delay from ORPC schema validation - Field stripping issues in sendMessage output - Auto-compaction trigger deletion ## What's different this time - Rebased onto latest main which includes fixes that were developed post-revert (model favorites, auto-compaction, etc.) - Conflict resolution preserves upstream features added after revert: - Workspace name collision retry with hash suffix - Mux Gateway coupon code handling with default models - AWS Bedrock credential nested structure ## Key Changes ### Architecture - New ORPC router (src/node/orpc/router.ts) - Central router with Zod schemas - Schema definitions (src/common/orpc/schemas.ts) - Shared validation - ServiceContainer (src/node/services/serviceContainer.ts) - DI container - React integration (src/browser/orpc/react.tsx) - ORPCProvider and useORPC() ### Transport - Desktop (Electron): MessagePort-based RPC via @orpc/server/message-port - Server mode: HTTP + WebSocket via @orpc/server/node and @orpc/server/ws - Auth middleware with timing-safe token comparison ### Removed - src/browser/api.ts (old HTTP/WS client) - src/node/services/ipcMain.ts (old IPC handler registration) - Old IPC method definitions in preload.ts --- _Generated with mux_ fix: add @babel/preset-react for JSX transpilation in Jest tests fix: add missing timestamp field to ToolCallEndEvent schema and usages fix: use ref for ORPCProvider cleanup to avoid stale closure The useEffect cleanup captured state at mount time ('connecting'), so even after transitioning to 'connected', cleanup never ran. Now stores cleanup function in a ref that's always current. test: add feature branch to storybook mock branch list Extend listBranches mock response with a feature branch example to better represent realistic branch listings during component development and testing. fix: batch async message yields to prevent scroll issues The oRPC async generator was yielding messages one at a time with async boundaries, causing premature React renders during history replay. This led to scroll-to-bottom firing before all messages loaded. Extract createAsyncMessageQueue utility that yields all queued messages synchronously (no async pauses within a batch). Used by both the real router and storybook mocks to match original IPC callback behavior. fix: add missing type discriminators to storybook chat messages The MarkdownTables story was emitting messages without the required `type: "message"` field. The `as WorkspaceChatMessage` type casts hid this from TypeScript, causing messages to not be recognized by the chat processor and rendering "No Messages Yet" instead. Also fix ChatEventProcessor tests that had invalid event shapes: - Remove invalid `role`/`timestamp` from stream-start events - Add missing `workspaceId`/`tokens` to delta events fix: use useLayoutEffect for initial scroll to fix Chromatic snapshots The scroll-to-bottom when workspace loads was using useEffect with requestAnimationFrame, which runs asynchronously after browser paint. Chromatic could capture the snapshot before scroll completed. Switch to useLayoutEffect which runs synchronously after DOM mutations but before paint, ensuring scroll happens before Chromatic captures. This also removes the need for the RAF wrapper since useLayoutEffect already guarantees DOM is ready. fix: add play functions to ensure Chromatic captures scrolled state - Add waitForChatScroll play function that waits for messages to load and scroll to complete before Chromatic takes screenshots - Add data-testid="message-window" to scroll container in AIView - Add data-testid="chat-message" to message wrappers for test queries - Apply play function to ActiveWorkspaceWithChat and MarkdownTables stories - Fix ProviderIcon lint error: remove redundant "mux-gateway" union type (already included in ProviderName) fix: resolve React Native typecheck errors - Add missing 'error' event handler in normalizeChatEvent.ts Converts error events to stream-error for mobile display - Fix result.metadata access in WorkspaceScreen.tsx ResultSchema wraps data, so access result.data.metadata not result.metadata - Add RequestInitWithDispatcher type in aiService.ts Extends RequestInit with undici-specific dispatcher property for Node.js fix: use queueMicrotask instead of setTimeout in story mocks Replace setTimeout(..., 100) with queueMicrotask() for message delivery in Storybook mocks. This ensures messages arrive synchronously (in the microtask queue) rather than 100ms later, eliminating timing races with Chromatic's screenshot capture. - All onChat mock callbacks now use queueMicrotask for message delivery - Add chromatic: { delay: 500 } as backup for ActiveWorkspaceWithChat and MarkdownTables stories - Fix import: use storybook/test instead of @storybook/test (Storybook 10) fix: remove deprecated @storybook/addon-interactions from config In Storybook 10, interaction testing is built into the core and the addon-interactions package was deprecated. Remove it from the addons list to fix build failures caused by version mismatch. fix: add semver 7.x to fix storybook build in CI Storybook 10 imports semver/functions/sort.js which only exists in semver 7.x. The root-level semver was 6.x (needed by babel), causing ESM resolution in Node 20.x to fail in CI. Adding semver ^7.x as a direct dependency forces the root-level version to 7.x, which is backwards compatible with 6.x consumers. fix: add missing fields to oRPC provider and workspace schemas The ProviderConfigInfoSchema was missing couponCodeSet (for Mux Gateway) and aws (for Bedrock). oRPC strips fields not in the schema, so these values were never reaching the frontend UI. Also fixes workspace.fork to use FrontendWorkspaceMetadataSchema instead of WorkspaceMetadataSchema, ensuring namedWorkspacePath is not stripped. refactor: consolidate provider types to single source of truth Eliminates triple-definition of ProviderConfigInfo/AWSCredentialStatus types that existed in providerService.ts, Settings/types.ts, and the oRPC schema. Now the Zod schema is the single source of truth, with TypeScript types derived via z.infer. Adds conformance tests that validate oRPC schemas preserve all fields when parsing - this would have caught the missing couponCodeSet/aws fields bug. refactor: improve tool part schema type safety and remove dead code 1. Refactor MuxToolPartSchema using extend pattern for DRY code: - Base schema shares common fields (type, toolCallId, toolName, input, timestamp) - Pending variant: state="input-available", no output field - Available variant: state="output-available", output required 2. Remove dead ReasoningStartEventSchema: - Schema was defined but never emitted by backend - Not in WorkspaceChatMessageSchema union - No type guard existed for it 3. Update MuxToolPart type to use Zod inference: - Type now properly discriminates based on state - Accessing output requires narrowing to output-available state refactor: simplify tool output redaction with type narrowing Remove explicit DynamicToolPart cast in favor of TypeScript's built-in type narrowing after the discriminant check. The type guard `part.type !== "dynamic-tool"` already narrows the type, making the cast redundant. Also eliminate intermediate variables (toolPart, redacted) by returning the new object directly, reducing visual noise. fix: resolve race condition in queued messages test Capture event count before interrupt to establish baseline, then poll for new events rather than waiting for next event. The clear event may arrive before or simultaneously with stream-abort, causing the previous waitForQueuedMessageEvent call to miss it or timeout. refactor: rename ORPC types and hooks to generic API naming Rename ORPCClient to APIClient, useORPC to useAPI, and ORPCProvider to APIProvider. Move the module from src/browser/orpc/react.tsx to src/browser/hooks/useAPI.tsx to better reflect its purpose as a general API client abstraction rather than being tied to the oRPC implementation detail. This decouples the public interface from the underlying transport mechanism, making future backend changes transparent to consumers. refactor: expose connection state from useAPI hook with discriminated union Changed useAPI from returning just the client to returning a discriminated union with connection state, enabling consumers to handle loading/error states with skeleton loaders instead of blocking the entire UI. Changes: - useAPI now returns `{ api, status, error, authenticate, retry }` - `api` is `APIClient | null` based on connection state (type-safe) - `status` is one of: "connecting", "connected", "auth_required", "error" - When `status === "connected"`, TypeScript knows `api` is non-null - Auth modal moved to App.tsx (rendered after all hooks) - Moved file from hooks/useAPI.tsx to contexts/API.tsx (follows codebase convention where Context+Provider+hook combinations live in contexts/) Updated ~60 consumer files with null guards before API calls.
This commit reintroduces the ORPC refactoring that was originally merged in #763 and subsequently reverted in #777 due to regressions. ## Original PR: #763 Replaces the custom IPC layer with oRPC for type-safe RPC between browser/renderer and backend processes. ## Why it was reverted (#777) The original migration caused regressions including: - Streaming content delay from ORPC schema validation - Field stripping issues in sendMessage output - Auto-compaction trigger deletion ## What's different this time - Rebased onto latest main which includes fixes that were developed post-revert (model favorites, auto-compaction, etc.) - Conflict resolution preserves upstream features added after revert: - Workspace name collision retry with hash suffix - Mux Gateway coupon code handling with default models - AWS Bedrock credential nested structure ## Key Changes ### Architecture - New ORPC router (src/node/orpc/router.ts) - Central router with Zod schemas - Schema definitions (src/common/orpc/schemas.ts) - Shared validation - ServiceContainer (src/node/services/serviceContainer.ts) - DI container - React integration (src/browser/orpc/react.tsx) - ORPCProvider and useORPC() ### Transport - Desktop (Electron): MessagePort-based RPC via @orpc/server/message-port - Server mode: HTTP + WebSocket via @orpc/server/node and @orpc/server/ws - Auth middleware with timing-safe token comparison ### Removed - src/browser/api.ts (old HTTP/WS client) - src/node/services/ipcMain.ts (old IPC handler registration) - Old IPC method definitions in preload.ts --- _Generated with mux_ fix: add @babel/preset-react for JSX transpilation in Jest tests fix: add missing timestamp field to ToolCallEndEvent schema and usages fix: use ref for ORPCProvider cleanup to avoid stale closure The useEffect cleanup captured state at mount time ('connecting'), so even after transitioning to 'connected', cleanup never ran. Now stores cleanup function in a ref that's always current. test: add feature branch to storybook mock branch list Extend listBranches mock response with a feature branch example to better represent realistic branch listings during component development and testing. fix: batch async message yields to prevent scroll issues The oRPC async generator was yielding messages one at a time with async boundaries, causing premature React renders during history replay. This led to scroll-to-bottom firing before all messages loaded. Extract createAsyncMessageQueue utility that yields all queued messages synchronously (no async pauses within a batch). Used by both the real router and storybook mocks to match original IPC callback behavior. fix: add missing type discriminators to storybook chat messages The MarkdownTables story was emitting messages without the required `type: "message"` field. The `as WorkspaceChatMessage` type casts hid this from TypeScript, causing messages to not be recognized by the chat processor and rendering "No Messages Yet" instead. Also fix ChatEventProcessor tests that had invalid event shapes: - Remove invalid `role`/`timestamp` from stream-start events - Add missing `workspaceId`/`tokens` to delta events fix: use useLayoutEffect for initial scroll to fix Chromatic snapshots The scroll-to-bottom when workspace loads was using useEffect with requestAnimationFrame, which runs asynchronously after browser paint. Chromatic could capture the snapshot before scroll completed. Switch to useLayoutEffect which runs synchronously after DOM mutations but before paint, ensuring scroll happens before Chromatic captures. This also removes the need for the RAF wrapper since useLayoutEffect already guarantees DOM is ready. fix: add play functions to ensure Chromatic captures scrolled state - Add waitForChatScroll play function that waits for messages to load and scroll to complete before Chromatic takes screenshots - Add data-testid="message-window" to scroll container in AIView - Add data-testid="chat-message" to message wrappers for test queries - Apply play function to ActiveWorkspaceWithChat and MarkdownTables stories - Fix ProviderIcon lint error: remove redundant "mux-gateway" union type (already included in ProviderName) fix: resolve React Native typecheck errors - Add missing 'error' event handler in normalizeChatEvent.ts Converts error events to stream-error for mobile display - Fix result.metadata access in WorkspaceScreen.tsx ResultSchema wraps data, so access result.data.metadata not result.metadata - Add RequestInitWithDispatcher type in aiService.ts Extends RequestInit with undici-specific dispatcher property for Node.js fix: use queueMicrotask instead of setTimeout in story mocks Replace setTimeout(..., 100) with queueMicrotask() for message delivery in Storybook mocks. This ensures messages arrive synchronously (in the microtask queue) rather than 100ms later, eliminating timing races with Chromatic's screenshot capture. - All onChat mock callbacks now use queueMicrotask for message delivery - Add chromatic: { delay: 500 } as backup for ActiveWorkspaceWithChat and MarkdownTables stories - Fix import: use storybook/test instead of @storybook/test (Storybook 10) fix: remove deprecated @storybook/addon-interactions from config In Storybook 10, interaction testing is built into the core and the addon-interactions package was deprecated. Remove it from the addons list to fix build failures caused by version mismatch. fix: add semver 7.x to fix storybook build in CI Storybook 10 imports semver/functions/sort.js which only exists in semver 7.x. The root-level semver was 6.x (needed by babel), causing ESM resolution in Node 20.x to fail in CI. Adding semver ^7.x as a direct dependency forces the root-level version to 7.x, which is backwards compatible with 6.x consumers. fix: add missing fields to oRPC provider and workspace schemas The ProviderConfigInfoSchema was missing couponCodeSet (for Mux Gateway) and aws (for Bedrock). oRPC strips fields not in the schema, so these values were never reaching the frontend UI. Also fixes workspace.fork to use FrontendWorkspaceMetadataSchema instead of WorkspaceMetadataSchema, ensuring namedWorkspacePath is not stripped. refactor: consolidate provider types to single source of truth Eliminates triple-definition of ProviderConfigInfo/AWSCredentialStatus types that existed in providerService.ts, Settings/types.ts, and the oRPC schema. Now the Zod schema is the single source of truth, with TypeScript types derived via z.infer. Adds conformance tests that validate oRPC schemas preserve all fields when parsing - this would have caught the missing couponCodeSet/aws fields bug. refactor: improve tool part schema type safety and remove dead code 1. Refactor MuxToolPartSchema using extend pattern for DRY code: - Base schema shares common fields (type, toolCallId, toolName, input, timestamp) - Pending variant: state="input-available", no output field - Available variant: state="output-available", output required 2. Remove dead ReasoningStartEventSchema: - Schema was defined but never emitted by backend - Not in WorkspaceChatMessageSchema union - No type guard existed for it 3. Update MuxToolPart type to use Zod inference: - Type now properly discriminates based on state - Accessing output requires narrowing to output-available state refactor: simplify tool output redaction with type narrowing Remove explicit DynamicToolPart cast in favor of TypeScript's built-in type narrowing after the discriminant check. The type guard `part.type !== "dynamic-tool"` already narrows the type, making the cast redundant. Also eliminate intermediate variables (toolPart, redacted) by returning the new object directly, reducing visual noise. fix: resolve race condition in queued messages test Capture event count before interrupt to establish baseline, then poll for new events rather than waiting for next event. The clear event may arrive before or simultaneously with stream-abort, causing the previous waitForQueuedMessageEvent call to miss it or timeout. refactor: rename ORPC types and hooks to generic API naming Rename ORPCClient to APIClient, useORPC to useAPI, and ORPCProvider to APIProvider. Move the module from src/browser/orpc/react.tsx to src/browser/hooks/useAPI.tsx to better reflect its purpose as a general API client abstraction rather than being tied to the oRPC implementation detail. This decouples the public interface from the underlying transport mechanism, making future backend changes transparent to consumers. refactor: expose connection state from useAPI hook with discriminated union Changed useAPI from returning just the client to returning a discriminated union with connection state, enabling consumers to handle loading/error states with skeleton loaders instead of blocking the entire UI. Changes: - useAPI now returns `{ api, status, error, authenticate, retry }` - `api` is `APIClient | null` based on connection state (type-safe) - `status` is one of: "connecting", "connected", "auth_required", "error" - When `status === "connected"`, TypeScript knows `api` is non-null - Auth modal moved to App.tsx (rendered after all hooks) - Moved file from hooks/useAPI.tsx to contexts/API.tsx (follows codebase convention where Context+Provider+hook combinations live in contexts/) Updated ~60 consumer files with null guards before API calls.
This commit reintroduces the ORPC refactoring that was originally merged in #763 and subsequently reverted in #777 due to regressions. ## Original PR: #763 Replaces the custom IPC layer with oRPC for type-safe RPC between browser/renderer and backend processes. ## Why it was reverted (#777) The original migration caused regressions including: - Streaming content delay from ORPC schema validation - Field stripping issues in sendMessage output - Auto-compaction trigger deletion ## What's different this time - Rebased onto latest main which includes fixes that were developed post-revert (model favorites, auto-compaction, etc.) - Conflict resolution preserves upstream features added after revert: - Workspace name collision retry with hash suffix - Mux Gateway coupon code handling with default models - AWS Bedrock credential nested structure ## Key Changes ### Architecture - New ORPC router (src/node/orpc/router.ts) - Central router with Zod schemas - Schema definitions (src/common/orpc/schemas.ts) - Shared validation - ServiceContainer (src/node/services/serviceContainer.ts) - DI container - React integration (src/browser/orpc/react.tsx) - ORPCProvider and useORPC() ### Transport - Desktop (Electron): MessagePort-based RPC via @orpc/server/message-port - Server mode: HTTP + WebSocket via @orpc/server/node and @orpc/server/ws - Auth middleware with timing-safe token comparison ### Removed - src/browser/api.ts (old HTTP/WS client) - src/node/services/ipcMain.ts (old IPC handler registration) - Old IPC method definitions in preload.ts --- _Generated with mux_ fix: add @babel/preset-react for JSX transpilation in Jest tests fix: add missing timestamp field to ToolCallEndEvent schema and usages fix: use ref for ORPCProvider cleanup to avoid stale closure The useEffect cleanup captured state at mount time ('connecting'), so even after transitioning to 'connected', cleanup never ran. Now stores cleanup function in a ref that's always current. test: add feature branch to storybook mock branch list Extend listBranches mock response with a feature branch example to better represent realistic branch listings during component development and testing. fix: batch async message yields to prevent scroll issues The oRPC async generator was yielding messages one at a time with async boundaries, causing premature React renders during history replay. This led to scroll-to-bottom firing before all messages loaded. Extract createAsyncMessageQueue utility that yields all queued messages synchronously (no async pauses within a batch). Used by both the real router and storybook mocks to match original IPC callback behavior. fix: add missing type discriminators to storybook chat messages The MarkdownTables story was emitting messages without the required `type: "message"` field. The `as WorkspaceChatMessage` type casts hid this from TypeScript, causing messages to not be recognized by the chat processor and rendering "No Messages Yet" instead. Also fix ChatEventProcessor tests that had invalid event shapes: - Remove invalid `role`/`timestamp` from stream-start events - Add missing `workspaceId`/`tokens` to delta events fix: use useLayoutEffect for initial scroll to fix Chromatic snapshots The scroll-to-bottom when workspace loads was using useEffect with requestAnimationFrame, which runs asynchronously after browser paint. Chromatic could capture the snapshot before scroll completed. Switch to useLayoutEffect which runs synchronously after DOM mutations but before paint, ensuring scroll happens before Chromatic captures. This also removes the need for the RAF wrapper since useLayoutEffect already guarantees DOM is ready. fix: add play functions to ensure Chromatic captures scrolled state - Add waitForChatScroll play function that waits for messages to load and scroll to complete before Chromatic takes screenshots - Add data-testid="message-window" to scroll container in AIView - Add data-testid="chat-message" to message wrappers for test queries - Apply play function to ActiveWorkspaceWithChat and MarkdownTables stories - Fix ProviderIcon lint error: remove redundant "mux-gateway" union type (already included in ProviderName) fix: resolve React Native typecheck errors - Add missing 'error' event handler in normalizeChatEvent.ts Converts error events to stream-error for mobile display - Fix result.metadata access in WorkspaceScreen.tsx ResultSchema wraps data, so access result.data.metadata not result.metadata - Add RequestInitWithDispatcher type in aiService.ts Extends RequestInit with undici-specific dispatcher property for Node.js fix: use queueMicrotask instead of setTimeout in story mocks Replace setTimeout(..., 100) with queueMicrotask() for message delivery in Storybook mocks. This ensures messages arrive synchronously (in the microtask queue) rather than 100ms later, eliminating timing races with Chromatic's screenshot capture. - All onChat mock callbacks now use queueMicrotask for message delivery - Add chromatic: { delay: 500 } as backup for ActiveWorkspaceWithChat and MarkdownTables stories - Fix import: use storybook/test instead of @storybook/test (Storybook 10) fix: remove deprecated @storybook/addon-interactions from config In Storybook 10, interaction testing is built into the core and the addon-interactions package was deprecated. Remove it from the addons list to fix build failures caused by version mismatch. fix: add semver 7.x to fix storybook build in CI Storybook 10 imports semver/functions/sort.js which only exists in semver 7.x. The root-level semver was 6.x (needed by babel), causing ESM resolution in Node 20.x to fail in CI. Adding semver ^7.x as a direct dependency forces the root-level version to 7.x, which is backwards compatible with 6.x consumers. fix: add missing fields to oRPC provider and workspace schemas The ProviderConfigInfoSchema was missing couponCodeSet (for Mux Gateway) and aws (for Bedrock). oRPC strips fields not in the schema, so these values were never reaching the frontend UI. Also fixes workspace.fork to use FrontendWorkspaceMetadataSchema instead of WorkspaceMetadataSchema, ensuring namedWorkspacePath is not stripped. refactor: consolidate provider types to single source of truth Eliminates triple-definition of ProviderConfigInfo/AWSCredentialStatus types that existed in providerService.ts, Settings/types.ts, and the oRPC schema. Now the Zod schema is the single source of truth, with TypeScript types derived via z.infer. Adds conformance tests that validate oRPC schemas preserve all fields when parsing - this would have caught the missing couponCodeSet/aws fields bug. refactor: improve tool part schema type safety and remove dead code 1. Refactor MuxToolPartSchema using extend pattern for DRY code: - Base schema shares common fields (type, toolCallId, toolName, input, timestamp) - Pending variant: state="input-available", no output field - Available variant: state="output-available", output required 2. Remove dead ReasoningStartEventSchema: - Schema was defined but never emitted by backend - Not in WorkspaceChatMessageSchema union - No type guard existed for it 3. Update MuxToolPart type to use Zod inference: - Type now properly discriminates based on state - Accessing output requires narrowing to output-available state refactor: simplify tool output redaction with type narrowing Remove explicit DynamicToolPart cast in favor of TypeScript's built-in type narrowing after the discriminant check. The type guard `part.type !== "dynamic-tool"` already narrows the type, making the cast redundant. Also eliminate intermediate variables (toolPart, redacted) by returning the new object directly, reducing visual noise. fix: resolve race condition in queued messages test Capture event count before interrupt to establish baseline, then poll for new events rather than waiting for next event. The clear event may arrive before or simultaneously with stream-abort, causing the previous waitForQueuedMessageEvent call to miss it or timeout. refactor: rename ORPC types and hooks to generic API naming Rename ORPCClient to APIClient, useORPC to useAPI, and ORPCProvider to APIProvider. Move the module from src/browser/orpc/react.tsx to src/browser/hooks/useAPI.tsx to better reflect its purpose as a general API client abstraction rather than being tied to the oRPC implementation detail. This decouples the public interface from the underlying transport mechanism, making future backend changes transparent to consumers. refactor: expose connection state from useAPI hook with discriminated union Changed useAPI from returning just the client to returning a discriminated union with connection state, enabling consumers to handle loading/error states with skeleton loaders instead of blocking the entire UI. Changes: - useAPI now returns `{ api, status, error, authenticate, retry }` - `api` is `APIClient | null` based on connection state (type-safe) - `status` is one of: "connecting", "connected", "auth_required", "error" - When `status === "connected"`, TypeScript knows `api` is non-null - Auth modal moved to App.tsx (rendered after all hooks) - Moved file from hooks/useAPI.tsx to contexts/API.tsx (follows codebase convention where Context+Provider+hook combinations live in contexts/) Updated ~60 consumer files with null guards before API calls.
## Summary Reintroduces the ORPC refactoring originally merged in #763 and reverted in #777. This replaces the custom IPC layer with [oRPC](https://orpc.unnoq.com/) for type-safe RPC between browser/renderer and backend processes. ## Why it was reverted (#777) The original migration caused regressions: - Streaming content delay from ORPC schema validation - Field stripping issues in sendMessage output - Auto-compaction trigger deletion ## What's different this time - Rebased onto latest main which includes fixes developed post-revert - Conflict resolution preserves upstream features added after revert: - Workspace name collision retry with hash suffix (#779) - Mux Gateway coupon code handling with default models - AWS Bedrock credential nested structure ## Key Changes ### Architecture - **New ORPC router** (`src/node/orpc/router.ts`) - Central router with Zod schemas - **Schema definitions** (`src/common/orpc/schemas.ts`) - Shared validation - **ServiceContainer** (`src/node/services/serviceContainer.ts`) - DI container - **React integration** (`src/browser/orpc/react.tsx`) - `ORPCProvider` and `useORPC()` ### Transport - **Desktop (Electron)**: MessagePort-based RPC via `@orpc/server/message-port` - **Server mode**: HTTP + WebSocket via `@orpc/server/node` and `@orpc/server/ws` - Auth middleware with timing-safe token comparison ### Removed - `src/browser/api.ts` (old HTTP/WS client) - `src/node/services/ipcMain.ts` (old IPC handler registration) - Old IPC method definitions in preload.ts ## Test plan - [x] Run `make typecheck` - passes locally - [x] Run `make test` - verify existing tests pass - [x] Manual testing of desktop app (Electron) - [x] Manual testing of server mode (browser) - [x] Verify streaming chat works without delays - [x] Verify auto-compaction triggers correctly --- _Generated with [mux](https://github.com/coder/mux)_
Summary
Replaces the custom IPC layer with oRPC for type-safe RPC between browser/renderer and backend processes.
Key Changes
Architecture
src/node/orpc/router.ts) - Central router defining all RPC endpoints with Zod schemassrc/common/orpc/schemas.ts) - Shared Zod schemas for request/response validationsrc/node/services/serviceContainer.ts) - Dependency injection container for all backend servicessrc/browser/orpc/react.tsx) -ORPCProvideranduseORPC()hook for frontendTransport
@orpc/server/message-port@orpc/server/nodeand@orpc/server/wsSubscriptions
Streaming endpoints (chat events, metadata updates, terminal output) use async generators:
Removed
src/browser/api.ts(old HTTP/WS client)src/node/services/ipcMain.ts(old IPC handler registration)src/desktop/preload.tsIPC method definitions (now just MessagePort forwarding)tests/ipcMain/directory (migrated totests/integration/)Testing
StreamCollectorutility for testing async generator subscriptionsMigration Notes
useORPC()hook instead ofwindow.apisetClient()during app initializationGenerated with mux